/*
*   多种唤醒方式(电压降唤醒NLP、定时唤醒TIMER-判断引擎是否启动JUGE)
*   OBD检查函数
*/
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "driver/rtc_io.h"
#include "nvs_flash.h"
#include "esp_sleep.h"
#include "esp_log.h"

#include "app_data.h"
#include "bsp_sleep.h"
#include "bsp_lcd.h"
#include "bsp_backlight.h"

#define ESP_INTR_FLAG_DEFAULT 0

static const char *TAG = "BSP_SLEEP";
static RTC_DATA_ATTR struct timeval sleep_enter_time;

bool app_sleep_judge(void);
static void bsp_sleep_read_data(void);

/** 配置并进入休眠模式 **/
void bsp_sleep_start(void)
{
    if (app_data.sleep_flag)
    {
        app_data.sleep_flag = 0;
    }

    //关闭外设、休眠前进行校准
    // bsp_motor_adjust();
    gpio_set_level(CONFIG_HUD_CAN_GPIO, 1); // 非必须休眠后会自动上拉
    bsp_lcd_alloff();
    bsp_lcd_poweron(0);
    bsp_backlight_stop();

    /** 设置从外部引脚唤醒 **/
    const int ext_wakeup_pin = CONFIG_HUD_DEFAULT_WAKEUP_PIN;
    const uint64_t ext_wakeup_pin_mask = 1ULL << ext_wakeup_pin;
    ESP_LOGI(TAG, "Enabling EXT1 wakeup on pins GPIO%d", ext_wakeup_pin);
    esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_mask, CONFIG_HUD_DEFAULT_WAKEUP_LEVEL);

    // 判断VIN电压大小
    if (app_data.sensor.vin > 11)
    {
        const int wakeup_time_sec = CONFIG_HUD_DEFAULT_WAKEUP_TIME;
        ESP_LOGI(TAG, "Enabling timer wakeup, %ds", wakeup_time_sec);
        esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000);
    }

    /**开始休眠**/
    rtc_gpio_isolate(GPIO_NUM_12);
    ESP_LOGI(TAG, "Entering deep sleep");
    uint32_t i = 20000000;
    while (i != 0)
        i--;
    gettimeofday(&sleep_enter_time, NULL);
    esp_deep_sleep_start();
}

/** 当系统被唤醒后执行 **/
void bsp_sleep_wakeup_info(void)
{
    //唤醒后检查引擎是否启动 检查电压
    struct timeval now;
    gettimeofday(&now, NULL);
    int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000;

    switch (esp_sleep_get_wakeup_cause())
    {
    case ESP_SLEEP_WAKEUP_EXT1:
    {
        //不判断 开启用户手动唤醒标志
        uint64_t wakeup_pin_mask = esp_sleep_get_ext1_wakeup_status();
        if (wakeup_pin_mask != 0)
        {
            int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
            ESP_LOGI(TAG, "Wake up from GPIO %d. Time spent in deep sleep: %dms", pin, sleep_time_ms);
        }
        else
        {
            ESP_LOGI(TAG, "Wake up from GPIO. Time spent in deep sleep: %dms", sleep_time_ms);
        }
        break;
    }
    case ESP_SLEEP_WAKEUP_TIMER:
        //进行判断是否睡眠
        ESP_LOGI(TAG, "Wake up from timer. Time spent in deep sleep: %dms", sleep_time_ms);
        if (app_sleep_judge())
        {
            printf("bsp sleep restart...\r\n");
            bsp_sleep_start();
        }
        break;
    case ESP_SLEEP_WAKEUP_ULP:
        ESP_LOGI(TAG, "Wake up from ULP. Time spent in deep sleep: %dms", sleep_time_ms);
        break;
    case ESP_SLEEP_WAKEUP_UNDEFINED:
        break;
    default:
        ESP_LOGI(TAG, "Not a deep sleep reset");
        break;
    }

    bsp_sleep_read_data();
}

/** 开关机判断 **/
bool app_sleep_judge(void)
{
    bool sleep_flag = 0;

    app_adc_read_analysis(); //更新ADC数据

    // 信号量等待OBD检查完成
    printf("app_data.sensor.vin:%f\r\n", app_data.sensor.vin);

    /* 高压或者过压则之间进入休眠 */
    if (app_data.sensor.vin > CONFIG_VIN_MAX || app_data.sensor.vin < CONFIG_VIN_MIN || app_data.obd.voltage > CONFIG_VIN_MAX || app_data.obd.voltage < CONFIG_VIN_MIN)
    {
        sleep_flag = 1;
    }
    else if ((app_data.obd.enable || (app_data.obd.rotation_enable && app_data.obd.rotation > 0)) && app_data.engine_status == 0)
    {
        //引擎启动，不睡眠
        sleep_flag = 0;
        app_data.engine_status = 1;
        app_data.engine_start_time = app_tick;
    }
    else if ((app_data.obd.enable == 0 || (app_data.obd.rotation_enable && app_data.obd.rotation == 0)) && app_data.engine_status == 1)
    {
        //引擎关闭，睡眠
        sleep_flag = 1;
        app_data.engine_status = 0;
        app_data.engine_stop_time = app_tick;
    }

    return sleep_flag;
}

static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    app_data.sleep_flag = 1;
}

void bsp_sleep_init(void)
{
    /* GPIO0 休眠与唤醒引脚*/
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_INTR_NEGEDGE;
    io_conf.pin_bit_mask = (1ULL << CONFIG_HUD_DEFAULT_WAKEUP_PIN);
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);

    /* GPIO2 CAN收发器使能引脚*/
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.pin_bit_mask = (1ULL << CONFIG_HUD_CAN_GPIO);
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);
    gpio_set_level(CONFIG_HUD_CAN_GPIO, 0); //使能收发器

    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    gpio_isr_handler_add(CONFIG_HUD_DEFAULT_WAKEUP_PIN, gpio_isr_handler, NULL);
}

/** 保存数据 **/
esp_err_t bsp_sleep_save_data(uint8_t num)
{
    /** 定时存储数据 储存数据 **/
    uint16_t temp;
    nvs_handle_t my_handle;
    esp_err_t err = nvs_open("storage", NVS_READWRITE, &my_handle);

    printf("sleep save data!\r\n");

    if (err != ESP_OK)
    {
        ESP_LOGI(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
    }
    else
    {
        switch (num)
        {
        case SAVE_BL:
            temp = app_data.light * 100;
            err = nvs_set_u16(my_handle, "backlight", temp);
            break;
        case SAVE_BL_MODE:
            err = nvs_set_u8(my_handle, "backlight_mode", app_data.light_mode);
            break;
        case SAVE_ANGLE:
            err = nvs_set_u8(my_handle, "standard_angle", app_data.standard_angle);
            break;
        case SAVE_CAN:
            err = nvs_set_u8(my_handle, "can_type", app_data.can_type);
            break;
        case SAVE_ZERO:
            err = nvs_set_u32(my_handle, "zero_degree", app_data.zero_degree);
            break;
        default:
            break;
        }
        if (err == ESP_OK)
        {
            err = nvs_commit(my_handle);
            nvs_close(my_handle);
        }
    }

    return err;
}

/** 读取数据 **/
static void bsp_sleep_read_data(void)
{
    nvs_handle_t my_handle;
    esp_err_t err = nvs_open("storage", NVS_READWRITE, &my_handle);

    if (err != ESP_OK)
    {
        ESP_LOGI(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
    }
    else
    {
        uint16_t temp;
        err = nvs_get_u16(my_handle, "backlight_duty", &temp);
        if (err != ESP_OK)
        {
            ESP_LOGI(TAG, "backlight_duty reading error (%s)", esp_err_to_name(err));
            app_data.backlight_duty = 0;
        }
        else
        {
            app_data.backlight_duty = temp / 100.0;
        }

        err = nvs_get_u8(my_handle, "backlight_mode", &app_data.light_mode);
        if (err != ESP_OK)
        {
            ESP_LOGI(TAG, "backlight_mode reading error (%s)", esp_err_to_name(err));
            app_data.light_mode = MANUAL;
        }

        err = nvs_get_u16(my_handle, "standard_angle", &app_data.standard_angle);
        if (err != ESP_OK)
        {
            ESP_LOGI(TAG, "standard_angle reading error (%s)", esp_err_to_name(err));
            app_data.standard_angle = 0;
        }

        err = nvs_get_u8(my_handle, "can_type", &app_data.can_type);
        if (err != ESP_OK)
        {
            ESP_LOGI(TAG, "can_type reading error (%s)", esp_err_to_name(err));
            app_data.can_type = 0;
        }

#ifdef CONFIG_HUD_USE_ADJUST
        err = nvs_get_u32(my_handle, "zero_degree", &app_data.zero_degree);
        if (err != ESP_OK)
        {
            ESP_LOGI(TAG, "zero_degree reading error (%s)", esp_err_to_name(err));
            app_data.zero_degree = 0;
        }
#endif

        nvs_close(my_handle);
    }
}